package com.province.platform.common.utils;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.province.platform.common.enums.CommonErrorCode;
import com.province.platform.common.exception.PlatformException;
import com.province.platform.common.spi.ErrorCode;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.io.File;
import java.io.IOException;
import java.util.*;

public class Configuration {
    private Object root = null;

    public static Configuration from(String configContent) {
        if (StringUtils.isBlank(configContent)) {
            throw PlatformException.asPlatformException(CommonErrorCode.CONFIG_ERROR, "配置信息不能为空白");
        }

        return new Configuration(configContent);
    }

    public static Configuration from(File file) {
        try {
            return Configuration.from(FileUtils.readFileToString(file, "UTF-8"));
        } catch (IOException e) {
            throw PlatformException.asPlatformException(CommonErrorCode.CONFIG_ERROR, String.format("配置文件[%s]不存在或读取失败", file.getAbsoluteFile()), e);
        }
    }

    public Object getContent() {
        return this.root;
    }

    public String getString(final String key) {
        Object value = this.get(key);
        if (Objects.isNull(value)) {
            return null;
        }
        return String.valueOf(value);
    }

    public Long getLong(final String key) {
        String value = this.getString(key);
        if (StringUtils.isBlank(value)) {
            return null;
        }

        try {
            return Long.valueOf(value);
        } catch (NumberFormatException e) {
            throw PlatformException.asPlatformException(CommonErrorCode.CONFIG_ERROR,
                    String.format("键[%s]值不是整数类型: %s", key, e.getMessage()));
        }
    }

    public Long getLong(final String key, long defaultValue) {
        Long value = this.getLong(key);
        if (Objects.isNull(value)) {
            return defaultValue;
        }
        return value;
    }

    public Integer getInt(final String key) {
        String value = this.getString(key);
        if (Objects.isNull(value)) {
            return null;
        }

        try {
            return Integer.valueOf(value);
        } catch (Exception e) {
            throw PlatformException.asPlatformException(CommonErrorCode.CONFIG_ERROR,
                    String.format("键[%s]值不是整数类型: %s", key, e.getMessage()));
        }
    }

    public Integer getInt(final String key, int defaultValue) {
        Integer value = this.getInt(key);
        if (Objects.isNull(value)) {
            return defaultValue;
        }
        return value;
    }

    @SuppressWarnings("unchecked")
    public <T> T get(final String key, Class<T> clazz) {
        this.checkKey(key);
        return (T) this.get(key);
    }

    public List<Object> getList(final String key) {
        List<Object> list = this.get(key, List.class);
        if (list == null || list.isEmpty()) {
            return null;
        }
        return list;
    }

    public List<Configuration> getListConfiguration(final String key) {
        List<Object> lists = this.getList(key);
        if (lists == null) {
            return null;
        }

        List<Configuration> result = new ArrayList<>();
        for (Object obj: lists) {
            result.add(Configuration.from(Configuration.toJSONString(obj)));
        }
        return result;

    }

    public String getNecessaryValue(String key, ErrorCode errorCode) {
        String value = this.getString(key);
        if (StringUtils.isBlank(value)) {
            throw PlatformException.asPlatformException(errorCode, String.format("配置文件有误,[%s]是必填参数，不允许为空或者留白", key));
        }
        return value;
    }

    public Configuration clone() {
        return Configuration.from(toJSONString(this.getContent()));
    }

    public Configuration getConfiguration(final String key) {
        Object object = this.get(key);
        if (Objects.isNull(object)) {
            return null;
        }

        return Configuration.from(Configuration.toJSONString(object));
    }

    /**
     * 根据用户提供的json path，寻址具体的对象。
     * <p/>
     * <br>
     * <p/>
     * NOTE: 目前仅支持Map以及List下标寻址, 例如:
     * <p/>
     * <br />
     * <p/>
     * 对于如下JSON
     * <p/>
     * {"a": {"b": {"c": [0,1,2,3]}}}
     * <p/>
     * config.get("") 返回整个Map <br>
     * config.get("a") 返回a下属整个Map <br>
     * config.get("a.b.c") 返回c对应的数组List <br>
     * config.get("a.b.c[0]") 返回数字0
     *
     * @return Java表示的JSON对象，如果path不存在或者对象不存在，均返回null。
     */
    public Object get(final String key) {
        this.checkKey(key);

        try {
            return this.findObject(key);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 根据用户提供的json path，插入指定对象，并返回之前存在的对象(如果存在)
     * <p/>
     * <br>
     * <p/>
     * 目前仅支持.以及数组下标寻址, 例如:
     * <p/>
     * <br />
     * <p/>
     * config.set("a.b.c[3]", object);
     * <p/>
     * <br>
     * 对于插入对象，Configuration不做任何限制，但是请务必保证该对象是简单对象(包括Map<String,
     * Object>、List<Object>)，不要使用自定义对象，否则后续对于JSON序列化等情况会出现未定义行为。
     *
     * @param key JSON path对象
     * @param obj 需要插入的对象
     * @return Java表示的JSON对象
     */
    public void set(final String key, final Object obj) {
        this.checkKey(key);

        setObject(key, extractConfiguration(obj));
    }

    /**
     * 合并其他Configuration，并修改两者冲突的KV配置
     *
     * @param another 合并加入的第三方Configuration
     * @param isUpdate 当合并双方出现KV冲突时候，选择更新当前KV，或者忽略该KV
     * @return 返回合并后对象
     */
    public Configuration merge(final Configuration another, boolean isUpdate) {
        Set<String> keys = another.getKeys();

        for (final String key: keys) {
            if (isUpdate) {
                this.set(key, another.get(key));
                continue;
            }

            if (Objects.nonNull(this.get(key))) {
                continue;
            }

            this.set(key, another.get(key));
        }

        return this;
    }

    private void setObject(final String key, final Object obj) {
        Object newRoot = this.setObjectRecursive(this.root, splitToList(key), 0, obj);
        if (Objects.nonNull(newRoot) && (newRoot instanceof Map || newRoot instanceof List)) {
            this.root = newRoot;
            return;
        }
        throw PlatformException.asPlatformException(CommonErrorCode.RUNTIME_ERROR,
                String.format("值[%s]无法适配您提供[%s]", ToStringBuilder.reflectionToString(obj), key));
    }

    @SuppressWarnings("unchecked")
    public Object setObjectRecursive(Object root, List<String> keys, int index, final Object value) {
        // 如果是已经超出key，我们就返回value即可，作为最底层叶子节点
        if (index == keys.size()) {
            return value;
        }

        String key = keys.get(index).trim();
        if (isMap(key)) {
            Map<String, Object> map;

            // 当前不是map，因此全部替换为map，并返回新建的map对象
            if (!(root instanceof Map)) {
                map = new HashMap<>();
                map.put(key, this.buildObject(keys.subList(index + 1, keys.size()), value));
                return map;
            }

            // 当前是map，但是没有对应的key，也就是我们需要新建对象插入该map，并返回该map
            map = ((Map<String, Object>) root);
            if (!map.containsKey(key)) {
                map.put(key, this.buildObject(keys.subList(index + 1, keys.size()), value));
                return map;
            }

            // 当前是map，而且还竟然存在这个值，好吧，继续递归遍历
            root = map.get(key);
            map.put(key, this.setObjectRecursive(root, keys, index + 1, value));
            return map;
        }

        if (isList(key)) {
            List<Object> list;
            int listIndex = this.getIndex(key);

            // 当前是list，直接新建并返回即可
            if (!(root instanceof List)) {
                list = this.expandList(new ArrayList<>(), listIndex + 1);
                list.set(listIndex, this.buildObject(keys.subList(index + 1, keys.size()), value));
                return list;
            }

            // 当前是list，但是对应的indexer是没有具体的值，也就是我们新建对象然后插入到该list，并返回该List
            list = (List<Object>) root;
            list = this.expandList(list, listIndex + 1);

            if (Objects.isNull(list.get(listIndex))) {
                list.set(listIndex, this.buildObject(keys.subList(index + 1, keys.size()), value));
                return list;
            }

            // 当前是list，并且存在对应的index，没有办法继续递归寻找
            root = list.get(listIndex);
            list.set(listIndex,
                    this.setObjectRecursive(root, keys, index + 1, value));
            return list;
        }

        throw PlatformException.asPlatformException(CommonErrorCode.RUNTIME_ERROR, String.format("Key[%s]格式有问题", key));
    }

    public Object buildObject(final List<String> keys, final Object value) {
        if (Objects.isNull(keys)) {
            throw PlatformException.asPlatformException(CommonErrorCode.RUNTIME_ERROR, "keys不能为空");
        }

        if (keys.size() == 1 && StringUtils.isBlank(keys.get(0))) {
            return value;
        }

        Object child = value;
        for (int i = keys.size() - 1; i >= 0; i--) {
            String key = keys.get(i);

            if (isMap(key)) {
                Map<String, Object> map = new HashMap<>();
                map.put(key, child);
                child = map;
                continue;
            }

            if (isList(key)) {
                List<Object> list = new ArrayList<>(this.getIndex(key) + 1);
                this.expandList(list, this.getIndex(key) + 1);
                list.set(this.getIndex(key), child);
                child = list;
                continue;
            }

            throw PlatformException.asPlatformException(CommonErrorCode.RUNTIME_ERROR,
                    String.format("Key[%s]出现非法类型[%s]", StringUtils.join(keys, "."), key));
        }

        return child;
    }

    private List<Object> expandList(final List<Object> list, final int size) {
        int expand = size - list.size();
        while (expand-- > 0) {
            list.add(null);
        }
        return list;
    }

    private int getIndex(final String index) {
        return Integer.valueOf(index.replace("[", "").replace("]", ""));
    }

    @SuppressWarnings("unchecked")
    private Object extractConfiguration(final Object obj) {
        if (obj instanceof Configuration) {
            return this.extractFromConfiguration(obj);
        }

        if (obj instanceof List) {
            List<Object> list = new ArrayList<>();
            for (final Object object: (List<Object>) obj) {
                list.add(this.extractFromConfiguration(object));
            }
            return list;
        }

        if (obj instanceof Map) {
            Map<String, Object> map = new HashMap<>();
            for (final String key: ((Map<String, Object>) obj).keySet()) {
                map.put(key, this.extractConfiguration(((Map<String, Object>) obj).get(key)));
            }
            return map;
        }
        return obj;
    }

    private Object extractFromConfiguration(Object obj) {
        if (obj instanceof Configuration) {
            return ((Configuration) obj).getContent();
        }
        return obj;
    }

    /**
     * 获取Configuration下所有叶子节点的key
     * <p/>
     * <br>
     * <p/>
     * 对于<br>
     * <p/>
     * {"a": {"b": {"c": [0,1,2,3]}}, "x": "y"}
     * <p/>
     * 下属的key包括: a.b.c[0],a.b.c[1],a.b.c[2],a.b.c[3],x
     */
    public Set<String> getKeys() {
        Set<String> collect = new HashSet<>();
        this.getKeysRecursive(this.getContent(), "", collect);
        return collect;
    }

    @SuppressWarnings({"unchecked", "ConstantValue"})
    private void getKeysRecursive(final Object obj, String path, Set<String> collect) {
        if (!(obj instanceof Map || obj instanceof List)) {
            collect.add(path);
            return;
        }

        if (obj instanceof Map) {
            Map<String, Object> map = (Map<String, Object>) obj;
            for (final String key: map.keySet()) {
                if (StringUtils.isBlank(key)) {
                    this.getKeysRecursive(map.get(key), key.trim(), collect);
                } else {
                    this.getKeysRecursive(map.get(key), path + "." + key.trim(), collect);
                }
            }
            return;
        }

        if (obj instanceof List) {
            List<Object> lists = (List<Object>) obj;
            for (int i = 0; i < lists.size(); i++) {
                this.getKeysRecursive(lists.get(i), path + String.format("[%s]", i), collect);
            }
        }
    }

    private Object findObject(final String key) {
        if (StringUtils.isBlank(key)) {
            return this.root;
        }

        Object target = this.root;

        for (final String item: this.splitToList(key)) {
            if (isMap(item)) {
                target = this.findObjectInMap(target, item);
            } else {
                target = this.findObjectInList(target, item);
            }
        }
        return target;
    }

    private boolean isMap(final String item) {
        return StringUtils.isNotBlank(item) && !this.isList(item);
    }

    private boolean isList(final String item) {
        return item.contains("[") && item.contains("]");
    }


    @SuppressWarnings("unchecked")
    private Object findObjectInList(Object target, final String key) {
        if (!(target instanceof List)) {
            throw PlatformException.asPlatformException(CommonErrorCode.CONFIG_ERROR,
                    String.format("配置文件有误，[%s]需要配置成json格式的Map对象，但实际类型是[%s]", key, target.getClass().toString()));
        }

        String index = key.replace("[", "").replace("]", "");
        if (!StringUtils.isNumeric(index)) {
            throw new IllegalArgumentException(String.format("下标必须为数字类型，但实际类型是[%s]", index));
        }

        return ((List<Object>) target).get(Integer.parseInt(index));
    }

    @SuppressWarnings("unchecked")
    private Object findObjectInMap(Object target, final String key) {
        if (!(target instanceof Map)) {
            throw PlatformException.asPlatformException(CommonErrorCode.CONFIG_ERROR,
                    String.format("配置文件有误，[%s]需要配置成json格式的Map对象，但实际类型是[%s]", key, target.getClass().toString()));
        }

        Object value = ((Map<String, Object>) target).get(key);
        Optional.ofNullable(value)
                .orElseThrow(() -> PlatformException.asPlatformException(CommonErrorCode.CONFIG_ERROR, String.format("键[%s]的值为空", key)));
        return value;
    }
    private List<String> splitToList(String key) {
        return Arrays.asList(StringUtils.split(split(key), "."));
    }

    private String split(final String key) {
        return StringUtils.replace(key, "[", ".[");
    }

    private void checkKey(final String key) {
        Optional.ofNullable(key).orElseThrow(()->new IllegalArgumentException("参数为空"));

        for (final String item: StringUtils.split(key, ".")) {
            if (StringUtils.isBlank(item)) {
                throw new IllegalArgumentException(String.format("路径[%s]出现空白字符串", key));
            }
        }
    }

    /**
     * 初始化空白的Configuration
     */
    public static Configuration newDefault() {
        return Configuration.from("{}");
    }

    private Configuration(final String json) {
        try {
            this.root = JSON.parse(json);
        } catch (Exception e) {
            throw PlatformException.asPlatformException(CommonErrorCode.CONFIG_ERROR, String.format("配置信息不是合法的json格式: %s", e.getMessage()));
        }
    }

    public void remove(final String key) {
        final Object object = this.get(key);
        Optional.ofNullable(object)
                .orElseThrow(() -> PlatformException.asPlatformException(CommonErrorCode.RUNTIME_ERROR, String.format("配置文件对应Key[%s]并不存在", key)));
        this.set(key, null);
    }

    public String toJSON() {
        return Configuration.toJSONString(this.getContent());
    }

    public String beautify() {
        return JSON.toJSONString(this.getContent(), JSONWriter.Feature.PrettyFormat);
    }

    public Configuration replaceVariable(final Configuration anther) {
        String json = anther.toJSON();
        Set<String> variables = StrUtils.getVariables(json);
        for (String key: variables) {
            Object object = this.get(key);
            Optional.ofNullable(object)
                    .orElseThrow(() -> PlatformException.asPlatformException(CommonErrorCode.RUNTIME_ERROR, String.format("配置文件对应Key[%s]并不存在", key)));
        }

        return this;
    }

    private static String toJSONString(final Object object) {
        return JSON.toJSONString(object);
    }
}
